/*
 * Flashlib - QB9 common Flash library
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.qb9.flashlib.logs
{
	import com.qb9.flashlib.interfaces.IDisposable;
	import com.qb9.flashlib.utils.ArrayUtil;
	
	import flash.utils.getQualifiedClassName;

	/** 
	 * Delegate the log messages destiny to its appenders.
	 *
	 * @see IAppender
	 */
	public class Logger implements IDisposable
	{
		public static const LOG_LEVEL_NONE:int = 0;
		public static const LOG_LEVEL_DEBUG:int = 10;
		public static const LOG_LEVEL_INFO:int = 20;
		public static const LOG_LEVEL_WARNING:int = 30;
		public static const LOG_LEVEL_ERROR:int = 40;
		
		private static var loggers:Object;
		
		protected var _logLevel:int;
		protected var appenders:Array;
		
		private var _name:String;

		/**
		 * @deprecated: Use Logger.getLogger("name"); instead of the constructor.
		 */
		[Deprecated(replacement="Use Logger.getLogger(className)")]
		public function Logger(level:int=LOG_LEVEL_DEBUG, name:String=""):void
		{
			_logLevel = level;
			appenders = [];
			_name = name;
		}

		public function addAppender(appender:IAppender):void
		{
			appenders.push(appender);
		}

		public function removeAppender(appender:IAppender):IAppender
		{
			return ArrayUtil.removeElement(appenders, appender) as IAppender;
		}

		/** 
		 * Creates (if it doesn't exists) and returns a new logger.
		 * The new logger is created with LOG_LEVEL_DEBUG.
		 * @param key Can be a string name or an object. If an object, then its class name will be used.
		 * @see changeLogLevel 
		 */
		public static function getLogger(key:Object):Logger
		{
			var name:String = getLoggerName(key);
			
			loggers = loggers || {};
			loggers[name] = loggers[name] || new Logger(LOG_LEVEL_DEBUG, name);			
			return loggers[name];
		}
		
		/**
		 * Removes the logger from the list and returns it.
		 * If name is not a registered Logger, nothing happens and null is returned.
		 * The logger isn't disposed as it could be used standalone.
		 */
		public static function removeLogger(key:Object):Logger
		{
			var name:String = getLoggerName(key);
			var logger:Logger = loggers[name];
			if (logger)
				delete loggers[name];	
			
			return logger;
		}
		
		private static function getLoggerName(key:Object):String
		{
			return key is String ? key.toString() : getQualifiedClassName(key)
		}
		
		/**
		 * Removes and disposes all loggers
		 */
		public static function removeAllLoggers():void
		{
			for each (var logger:Logger in loggers)
				logger.dispose();
			
			loggers = {};
		}

		public function get name():String
		{
			return _name;
		}

		/**
		 * Changes the log level.
		 *
		 * <p>The levels could be any of the following:
		 * LOG_LEVEL_NONE, LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_WARNING or LOG_LEVEL_ERROR.</p>
		*/
		public function changeLogLevel(level:int):void
		{
			_logLevel = level;
		}
		
		public function getLogLevel():int
		{
			return _logLevel;
		}

		public function error(...args):void
		{
			log(LOG_LEVEL_ERROR, "[ERROR]", args);
		}

		public function warning(...args):void
		{
			log(LOG_LEVEL_WARNING, "[WARNING]", args);
		}

		public function debug(...args):void
		{
			log(LOG_LEVEL_DEBUG, "[DEBUG]", args);
		}

		public function info(...args):void
		{
			log(LOG_LEVEL_INFO, "[INFO]", args);
		}

		protected function log(level:int, preffix:String, args:Array):void
		{
			if (_logLevel > level)
				return;
			
			args.unshift(preffix);
			for each (var appender:IAppender in appenders)
				appender.append(args, level);
		}
		
		public function dispose():void
		{
			for each (var appender:IAppender in appenders)
				if (appender is IDisposable)
					IDisposable(appender).dispose();
					
			appenders = null;
		}
	}
}
